{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 一篇搞定 OPENAI 的 Function calling"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> 函数 （Function Calling) 调用提供了一种将 GPT 的能力与外部工具和 API 相连接的新方法。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个文章中，我想向您展示 OpenAI 模型的函数 （Function Calling) 调用能力，并向您展示如何将此新功能与 Langchain 集成。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我将通过以下代码详细介绍这个工作原理，开始吧！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: langchain in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (0.0.222)\n",
      "Requirement already satisfied: PyYAML>=5.4.1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (6.0)\n",
      "Requirement already satisfied: SQLAlchemy<3,>=1.4 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (2.0.16)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (3.8.4)\n",
      "Requirement already satisfied: dataclasses-json<0.6.0,>=0.5.7 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (0.5.8)\n",
      "Requirement already satisfied: langchainplus-sdk>=0.0.17 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (0.0.19)\n",
      "Requirement already satisfied: numexpr<3.0.0,>=2.8.4 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (2.8.4)\n",
      "Requirement already satisfied: numpy<2,>=1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (1.25.0)\n",
      "Requirement already satisfied: openapi-schema-pydantic<2.0,>=1.2 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (1.2.4)\n",
      "Requirement already satisfied: pydantic<2,>=1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (1.10.9)\n",
      "Requirement already satisfied: requests<3,>=2 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (2.31.0)\n",
      "Requirement already satisfied: tenacity<9.0.0,>=8.1.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from langchain) (8.2.2)\n",
      "Requirement already satisfied: attrs>=17.3.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.1.0)\n",
      "Requirement already satisfied: charset-normalizer<4.0,>=2.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (3.1.0)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.4)\n",
      "Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (4.0.2)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.2)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.3)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
      "Requirement already satisfied: marshmallow<4.0.0,>=3.3.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from dataclasses-json<0.6.0,>=0.5.7->langchain) (3.19.0)\n",
      "Requirement already satisfied: marshmallow-enum<2.0.0,>=1.5.1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from dataclasses-json<0.6.0,>=0.5.7->langchain) (1.5.1)\n",
      "Requirement already satisfied: typing-inspect>=0.4.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from dataclasses-json<0.6.0,>=0.5.7->langchain) (0.9.0)\n",
      "Requirement already satisfied: typing-extensions>=4.2.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from pydantic<2,>=1->langchain) (4.6.3)\n",
      "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from requests<3,>=2->langchain) (3.4)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from requests<3,>=2->langchain) (1.26.16)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from requests<3,>=2->langchain) (2023.5.7)\n",
      "Requirement already satisfied: greenlet!=0.4.17 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from SQLAlchemy<3,>=1.4->langchain) (2.0.2)\n",
      "Requirement already satisfied: packaging>=17.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from marshmallow<4.0.0,>=3.3.0->dataclasses-json<0.6.0,>=0.5.7->langchain) (23.1)\n",
      "Requirement already satisfied: mypy-extensions>=0.3.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from typing-inspect>=0.4.0->dataclasses-json<0.6.0,>=0.5.7->langchain) (1.0.0)\n",
      "Note: you may need to restart the kernel to use updated packages.\n",
      "Requirement already satisfied: python-dotenv in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (1.0.0)\n",
      "Note: you may need to restart the kernel to use updated packages.\n",
      "Requirement already satisfied: openai in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (0.27.8)\n",
      "Requirement already satisfied: requests>=2.20 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from openai) (2.31.0)\n",
      "Requirement already satisfied: tqdm in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from openai) (4.65.0)\n",
      "Requirement already satisfied: aiohttp in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from openai) (3.8.4)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from requests>=2.20->openai) (3.1.0)\n",
      "Requirement already satisfied: idna<4,>=2.5 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from requests>=2.20->openai) (3.4)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from requests>=2.20->openai) (1.26.16)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from requests>=2.20->openai) (2023.5.7)\n",
      "Requirement already satisfied: attrs>=17.3.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp->openai) (23.1.0)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp->openai) (6.0.4)\n",
      "Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp->openai) (4.0.2)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp->openai) (1.9.2)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp->openai) (1.3.3)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from aiohttp->openai) (1.3.1)\n",
      "Requirement already satisfied: colorama in c:\\users\\freestone\\appdata\\local\\packages\\pythonsoftwarefoundation.python.3.11_qbz5n2kfra8p0\\localcache\\local-packages\\python311\\site-packages (from tqdm->openai) (0.4.6)\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install --upgrade langchain\n",
    "%pip install python-dotenv\n",
    "%pip install openai"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 安装包测试版本号"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " The version of the  langchain library is 0.0.222.\n"
     ]
    }
   ],
   "source": [
    "import pkg_resources\n",
    "\n",
    "\n",
    "def print_version(package_name):\n",
    "    try:\n",
    "        version = pkg_resources.get_distribution(package_name).version\n",
    "        print(f\" The version of the {package_name} library is {version}.\")\n",
    "    except pkg_resources.DistributionNotFound:\n",
    "        print(f\" The {package_name} library is not installed.\")\n",
    "\n",
    "\n",
    "print_version(\" langchain\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 导入密钥，连接 OpenAI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "import os\n",
    "import openai\n",
    "import json\n",
    "\n",
    "load_dotenv('.env.local')\n",
    "openai.api_key = os.environ.get(\"OPENAI_API_TOKEN\")\n",
    "print(f\"{openai.api_key}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义一个 Function Calling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_pizza_info(pizza_name: str):\n",
    "    pizza_info = {\n",
    "        \" name\": pizza_name,\n",
    "        \" price\": \" 10.99\",\n",
    "    }\n",
    "    return json.dumps(pizza_info)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "functions = [\n",
    " \n",
    "    {\n",
    "        \" name\": \" get_pizza_info\",\n",
    "        \" description\": \" Get name and price of a pizza of the restaurant\",\n",
    "        \" parameters\": {\n",
    "            \" type\": \" object\",\n",
    "            \" properties\": {\n",
    "                \" pizza_name\": {\n",
    "                    \" type\": \" string\",\n",
    "                    \" description\": \" The name of the pizza, e.g. Salami\",\n",
    "                },\n",
    "            },\n",
    "            \" required\": [\"pizza_name\"],\n",
    "        },\n",
    "    }\n",
    "]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 运行第一个 Function Calling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(query):\n",
    "    response = openai.ChatCompletion.create(\n",
    "        model =\" gpt-3.5-turbo-0613\",\n",
    "        messages = [{\"role\": \"user\", \"content\": query}],\n",
    "        functions = functions,\n",
    "    )\n",
    "    message = response [\"choices\"][0] [\"message\"]\n",
    "    return message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat(\" What is the capital of france?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<OpenAIObject at 0x166d47a43b0> JSON: {\n",
       "  \" role\": \" assistant\",\n",
       "  \" content\": null,\n",
       "  \" function_call\": {\n",
       "    \" name\": \" get_pizza_info\",\n",
       "    \" arguments\": \"{\\n\\\"pizza_name\\\": \\\"Salami\\\"\\n}\"\n",
       "  }\n",
       "}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "query = \" How much does pizza salami cost?\"\n",
    "message = chat(query)\n",
    "message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Salami\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<OpenAIObject chat.completion id=chatcmpl-7Y9045lCV15L1psS5SNYclk4SGcDU at 0x166c574fa10> JSON: {\n",
       "  \" id\": \" chatcmpl-7Y9045lCV15L1psS5SNYclk4SGcDU\",\n",
       "  \" object\": \" chat.completion\",\n",
       "  \" created\": 1688372104,\n",
       "  \" model\": \" gpt-3.5-turbo-0613\",\n",
       "  \" choices\": [\n",
       " \n",
       "    {\n",
       "      \" index\": 0,\n",
       "      \" message\": {\n",
       "        \" role\": \" assistant\",\n",
       "        \" content\": \" The cost of a pizza salami is $10.99.\"\n",
       " \n",
       "      },\n",
       "      \" finish_reason\": \" stop\"\n",
       "    }\n",
       "  ],\n",
       "  \" usage\": {\n",
       "    \" prompt_tokens\": 58,\n",
       "    \" completion_tokens\": 13,\n",
       "    \" total_tokens\": 71\n",
       "  }\n",
       "}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "if message.get(\" function_call\"):\n",
    "    # 解析第一次调用的时候返回的 pizza 信息\n",
    "    function_name = message [\"function_call\"][\"name\"]\n",
    "    pizza_name = json.loads(message [\"function_call\"] [\"arguments\"]).get(\" pizza_name\")\n",
    "    print(pizza_name)\n",
    "    # 这里将 chat 小助手函数的响应结果提取后，传递 function_response\n",
    "    function_response = get_pizza_info(\n",
    "        pizza_name = pizza_name \n",
    "    )\n",
    "\n",
    "    second_response = openai.ChatCompletion.create(\n",
    "        model =\" gpt-3.5-turbo-0613\",\n",
    "        messages =[\n",
    " \n",
    "            {\" role\": \" user\", \" content\": query},\n",
    "            message,\n",
    "            {\n",
    "                \" role\": \" function\",\n",
    "                \" name\": function_name,\n",
    "                \" content\": function_response, # function calling 的 content 是 get_pizza_info 函数 \n",
    "            },\n",
    "        ],\n",
    "    )\n",
    "\n",
    "second_response"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 如何与 LangChain 一起使用?"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先导入 ChatOpenAI 类和 HumanMessage、AIMessage，还有 ChatMessage 类，这些类可以帮助我们创建这种功能，包括用户角色等。\n",
    "我们可以不必要像之前那样，定义角色等，只需要传递 `content`。其他的都交给了 Langchain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.schema import HumanMessage, AIMessage, ChatMessage"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "先问问法国的首都，测试：What is the capital of france?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content ='The capital of France is Paris.', additional_kwargs ={}, example = False)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "llm = ChatOpenAI(model =\" gpt-3.5-turbo-0613\")\n",
    "message = llm.predict_messages(\n",
    "    [HumanMessage(content =\"What is the capital of france?\")], functions = functions\n",
    ")\n",
    "message"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们再次运行查询披萨莎拉米在餐厅里的价格。How much does pizza salami cost?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content ='', additional_kwargs ={'function_call': {'name': 'get_pizza_info', 'arguments': '{\\n\" pizza_name\": \" Salami\"\\n}'}}, example = False)"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "llm = ChatOpenAI(model =\" gpt-3.5-turbo-0613\")\n",
    "message_pizza = llm.predict_messages(\n",
    "    [HumanMessage(content =\"How much does pizza salami cost?\")], functions = functions\n",
    ")\n",
    "message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Salami'"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "pizza_name = json.loads(message_pizza.additional_kwargs [\"function_call\"][\"arguments\"]).get(\" pizza_name\")\n",
    "pizza_name"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后我们再次调用这个函数 （Function Calling) ，就像这样再次调用函数 （Function Calling) ，我们得到名称莎拉米和价格为 10.99。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'{\" name\": \" Salami\", \" price\": \" 10.99\"}'"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pizza_api_response = get_pizza_info(pizza_name = pizza_name)\n",
    "pizza_api_response"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们再次运行这个，我们应该得到与之前类似的响应，`AIMessage` 内容是在餐厅里的披萨价格为 `10.99`，因此非常容易使用."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       " AIMessage(content ='The pizza Salami costs $10.99.', additional_kwargs ={}, example = False)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "second_response = llm.predict_messages(\n",
    "    [\n",
    " \n",
    "        HumanMessage(content = query),\n",
    "        AIMessage(content = str(message_pizza.additional_kwargs)),\n",
    "        ChatMessage(\n",
    "            role =\" function\",\n",
    "            additional_kwargs ={\n",
    "                \" name\": message_pizza.additional_kwargs [\"function_call\"][\"name\"]\n",
    "            },\n",
    "            content = pizza_api_response\n",
    "        ),\n",
    "    ],\n",
    "    functions = functions,\n",
    ")\n",
    "second_response"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用 LangChain 的 tools"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "LangChain 已经提供了与外部世界交互的另一种标准化方法，以进行请求或其他操作，这些称为工具 tools，工具 tools 是由 Chain 提供的类，您也可以创建自己的工具，我将向您展示如何做到这一点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Optional\n",
    "from langchain.tools import BaseTool\n",
    "from langchain.callbacks.manager import (\n",
    "    AsyncCallbackManagerForToolRun,\n",
    "    CallbackManagerForToolRun,\n",
    ")\n",
    "\n",
    "\n",
    "class StupidJokeTool(BaseTool):\n",
    "    name = \" StupidJokeTool\"\n",
    "    description = \" Tool to explain jokes about chickens\"\n",
    "\n",
    "    def _run(\n",
    " \n",
    "        self, query: str, run_manager: Optional [CallbackManagerForToolRun] = None\n",
    "    ) -> str:\n",
    "        return \" It is funny, because AI...\"\n",
    "\n",
    "    async def _arun(\n",
    " \n",
    "        self, query: str, run_manager: Optional [AsyncCallbackManagerForToolRun] = None\n",
    "    ) -> str:\n",
    "        \"\"\" Use the tool asynchronously.\"\"\"\n",
    "        raise NotImplementedError(\" joke tool does not support async\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果您有了自己的工具与类一起使用，您可以轻松将自己的类转换为格式化的工具: `format_tool_to_openai_function`，我还在这里导入了 `MoveFileTool` 工具，它允许您在计算机上移动文件。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content ='', additional_kwargs ={'function_call': {'name': 'StupidJokeTool', 'arguments': '{\\n\"__arg1\": \" To get to the other side\"\\n}'}}, example = False)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.tools import format_tool_to_openai_function, MoveFileTool\n",
    "\n",
    "\n",
    "tools = [StupidJokeTool(), MoveFileTool()]\n",
    "# 将自己的 tools 转换为格式化的 function\n",
    "functions = [format_tool_to_openai_function(t) for t in tools]\n",
    "# functions 是之前定义的一个变量：一个函数列表\n",
    "\n",
    "query = \" Why does the chicken cross the road? To get to the other side\"\n",
    "output = llm.predict_messages([HumanMessage(content = query)], functions = functions)\n",
    "output"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "现在我们可以再次像这样使用它，例如为什么鸡过马路？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content ='', additional_kwargs ={'function_call': {'name': 'StupidJokeTool', 'arguments': '{\\n  \"__arg1\": \" To get to the other side\"\\n}'}}, example = False)"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "query = \" Why does the chicken cross the road? To get to the other side\"\n",
    "second_response = llm.predict_messages(\n",
    "    [\n",
    " \n",
    "        HumanMessage(content = query),\n",
    "        AIMessage(content = str(output.additional_kwargs)),\n",
    "        ChatMessage(\n",
    "            role =\" function\",\n",
    "            additional_kwargs ={\n",
    "                \" name\": output.additional_kwargs [\"function_call\"][\"name\"]\n",
    "            },\n",
    "            content =\"\"\"\n",
    " \n",
    "                {tool_response}\n",
    "            \"\"\",\n",
    " \n",
    "        ),\n",
    "    ],\n",
    "    functions = functions,\n",
    ")\n",
    "second_response"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Langchain Agent 如何实现 Function Calling ？"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我将向您展示它是如何工作的，首先我们导入一些链 Chain，例如 `LLMMathChain`，还有一个 `chat_models`，聊天模型在这里使用 `ChatOpenAI` 创建我们的 LLM。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain import LLMMathChain\n",
    "from langchain.agents import initialize_agent, Tool\n",
    "from langchain.agents import AgentType\n",
    "from langchain.chat_models import ChatOpenAI\n",
    "\n",
    "\n",
    "llm = ChatOpenAI(temperature = 0, model =\" gpt-3.5-turbo-0613\")\n",
    "llm_math_chain = LLMMathChain.from_llm(llm = llm, verbose = True)\n",
    " tools = [\n",
    " \n",
    "    Tool(\n",
    "        name =\" Calculator\",\n",
    "        func = llm_math_chain.run,\n",
    "        description =\" useful for when you need to answer questions about math\"\n",
    "    ),\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = initialize_agent(tools, llm, agent = AgentType.OPENAI_FUNCTIONS, verbose = True)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在我们运行“法国的首都是什么”，我们得到的结果是法国的首都：巴黎。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m > Entering new  chain...\u001b[0m\n",
      " \n",
      "\u001b[32; 1m\u001b[1; 3mThe capital of France is Paris.\u001b[0m\n",
      " \n",
      "\n",
      "\u001b[1m > Finished chain.\u001b[0m\n",
      " "
     ]
    },
    {
     "data": {
      "text/plain": [
       "'The capital of France is Paris.'"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent.run(\" What is the capital of france?\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果我们想知道 100 除以 25 等于多少，这时候计算器被调用，我们得到最终答案 100 除以 25 等于 4。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m > Entering new  chain...\u001b[0m\n",
      " \n",
      "\u001b[32; 1m\u001b[1; 3m\n",
      " \n",
      "Invoking: `Calculator` with `100 / 25`\n",
      "\n",
      "\n",
      "\u001b[0m\n",
      " \n",
      "\n",
      "\u001b[1m > Entering new  chain...\u001b[0m\n",
      " \n",
      " 100 / 25\u001b[32; 1m\u001b[1; 3m```text\n",
      " \n",
      "100 / 25\n",
      " ```\n",
      "...numexpr.evaluate(\"100 / 25\")...\n",
      "\u001b[0m\n",
      "Answer: \u001b[33;1m\u001b[1;3m4.0\u001b[0m\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\u001b[36;1m\u001b[1;3mAnswer: 4.0\u001b[0m\u001b[32;1m\u001b[1;3m100 除以 25 等于 4。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'100 除以 25 等于 4。'"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent.run(\"100 除以 25 等于多少?\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
